#pragma once

#include "znx_http_base.h"
#include "znx_http_conn.h"
#include "znx_http_errno.h"
#include "znx_http_utils.h"
#include "znx_http_parse.h"
#include "znx_http_headers.h"
#include "conf/znx_http_conf.h"
#include "event/znx_event_loop.h"
#include "comm/znx_buf.h"


typedef void (*znx_http_request_handler_t)(znx_http_request_t *request, znx_result_t res);


struct znx_http_response_s {
    znx_http_request_t                      *request;

    // context
    void                                    *data;
    znx_http_request_handler_t              handler;

    // the http response line & headers is saved in multiple bufs.
    // these buffers cannot be released during the request process.
    // we will release it at the end of the request.
    znx_buf_chain_t                         *header_chain;
    size_t                                  header_size;

    znx_buf_chain_t                         *body_chain;
    znx_buf_t                               *body_buf;

    // point to the buffer currently being written,
    // it is only a reference.
    znx_buf_t                               *last_buf;

    int                                     status_code;
    znx_str_t                               status_reason;
    znx_http_headers_t                      headers;
    znx_http_trailers_t                     *trailers;

    int64_t                                 content_length;
    int64_t                                 content_remain;

    int                                     invalid_headers;

    unsigned                                header_sent:1;
    unsigned                                nobody:1;
    unsigned                                chunked:1;
    unsigned                                start_send_body:1;
    unsigned                                send_body_done:1;
    unsigned                                last_body_flag:1;
    unsigned                                write_body_done:1;
    unsigned                                start_read_header:1;
    unsigned                                parse_rl_done:1;
    unsigned                                parse_rh_done:1;
    unsigned                                start_read_body:1;
    unsigned                                read_body_done:1;
};


struct znx_http_request_s {
    znx_http_core_conf_t                    *cf;
    znx_http_conn_t                         *hc;
    znx_pool_t                              *pool;
    znx_http_response_t                     *response;

    // context
    void                                    *data;
    znx_http_request_handler_t              handler;
    znx_http_request_handler_t              finalized_handler;

    // the http request line & headers is saved in multiple bufs.
    // these buffers cannot be released during the request process.
    // we will release it at the end of the request.
    znx_buf_chain_t                         *header_chain;
    size_t                                  header_size;

    znx_buf_chain_t                         *body_chain;
    znx_buf_t                               *body_buf;

    // point to the buffer currently being written,
    // it is only a reference.
    znx_buf_t                               *last_buf;

    // for send request or response data.
    znx_buf_chain_t                         *output_chain;

    // for request parse.
    znx_http_readline_sm_t                  readline_sm;

    znx_str_t                               method;
    // request_uri = uri + '?' args + '#' + fragment.
    znx_str_t                               request_uri;
    znx_str_t                               uri;
    znx_str_t                               args;
    znx_str_t                               fragment;

    // for full request url.
    znx_str_t                               scheme;
    znx_str_t                               host;
    znx_str_t                               port;

    int                                     http_version;
    int                                     invalid_headers;

    znx_http_headers_t                      headers;
    znx_http_trailers_t                     *trailers;
    int64_t                                 content_length;
    int64_t                                 content_remain;

    znx_http_chunk_sm_t                     *chunk_sm;

    // record errors that occur on the http request.
    znx_result_t                            read_error;
    znx_result_t                            write_error;

    // http client or server request.
    unsigned                                client_request:1;
    // parse request line done.
    unsigned                                parse_rl_done:1;
    // parse request header done.
    unsigned                                parse_rh_done:1;
    // identifies whether the request can be keep-alive. 
    unsigned                                want_close:1;
    unsigned                                nobody:1;
    unsigned                                chunked:1;
    unsigned                                start_read_header:1;
    unsigned                                start_read_body:1;
    unsigned                                read_body_done:1;
    unsigned                                read_trailers:1;
    unsigned                                header_sent:1;
    unsigned                                start_send_body:1;
    unsigned                                send_body_done:1;
    unsigned                                last_body_flag:1;
    unsigned                                write_body_done:1;
    unsigned                                is_websocket:1;
};

//////////////////////// common interface ////////////////////////

znx_http_request_t *znx_http_request_create(znx_pool_t *pool,
    znx_http_conn_t *hc, znx_bool_t client_request);

void znx_http_request_destroy(znx_http_request_t *request);

znx_bool_t znx_http_body_allowed_for_status(int status_code);

////////////////////// http server interface /////////////////////

void znx_http_start_read_request_headers(znx_http_request_t *request,
    znx_http_request_handler_t done_handler, void *data);

void znx_http_start_read_request_body(znx_http_request_t *request,
    znx_http_request_handler_t notify_handler, void *data);

void znx_http_enable_read_request_body(znx_http_request_t *request);

void znx_http_disable_read_request_body(znx_http_request_t *request);

// pack the response header into buf and try to call write once.
znx_result_t znx_http_send_response_header(znx_http_request_t *request);

void znx_http_start_send_response_body(znx_http_request_t *request,
    znx_http_request_handler_t notify_handler, void *data);

void znx_http_enable_send_response_body(znx_http_request_t *request);

void znx_http_disable_send_response_body(znx_http_request_t *request);

void znx_http_set_send_response_body_done(znx_http_request_t *request);

///////////////////////// http client interface /////////////////////

// pack the request header into buf and try to call write once.
znx_result_t znx_http_send_request_header(znx_http_request_t *request);

void znx_http_start_send_request_body(znx_http_request_t *request,
    znx_http_request_handler_t notify_handler, void *data);

void znx_http_enable_send_request_body(znx_http_request_t *request);

void znx_http_disable_send_request_body(znx_http_request_t *request);

void znx_http_set_send_request_body_done(znx_http_request_t *request);

void znx_http_start_read_response_headers(znx_http_request_t *request,
    znx_http_request_handler_t done_handler, void *data);

void znx_http_start_read_response_body(znx_http_request_t *request,
    znx_http_request_handler_t notify_handler, void *data);

void znx_http_enable_read_response_body(znx_http_request_t *request);

void znx_http_disable_read_response_body(znx_http_request_t *request);

////////////////////////// for http test ////////////////////////////

void znx_http_request_dump(znx_http_request_t *request);

znx_result_t znx_http_seal_response_header(znx_http_request_t *request,
    znx_buf_t **header_buf_p);

znx_result_t znx_http_seal_request_header(znx_http_request_t *request,
    znx_buf_t **header_buf_p);